#include <iostream>     // std::cout
#include <algorithm>    // std::sort std::copy std::merge
#include <vector>       // std::vector
#include "UniformRealDistribution.h"
using namespace std;

class Leo {
private:
    double d;
    int i;

public:

    Leo() {
        cout << "Ctor1" << endl;
        d = UniformRealDistribution::rand(-10, 10);
        i = (int) UniformRealDistribution::rand(-10, 10);
    }

    Leo(double val) {
        cout << "Ctor2" << endl;
        d = val;
        i = (int) val;
    }

    Leo(double d, int i) {
        cout << "Ctor3" << endl;
        this->d = d + 0.01;
        this->i = i;
    }

    virtual ~Leo() {
        cout << "Lídia bobona" << endl;
    }

    void Print() {
        std::cout << "(" << d << ", " << i << ")" << std::endl;
    }

    double valDouble() {
        return d;
    }

    int valInt() {
        return i;
    }

    bool operator<(Leo other) {
        return this->d < other.valDouble();
    }
};

void sortAlgorithmExample() {
    std::vector<Leo*> v1;

    for (int i = 0; i < 4; i++) v1.push_back(new Leo);
    cout << "v1 antes de ordenar" << endl; for (int i = 0; i < 4; i++) v1.at(i)->Print();
    std::sort(v1.begin(), v1.end(), [](Leo* a, Leo * b) {return *a < *b;});
    cout << "\nv1 após ordenar" << endl;
    for (int i = 0; i < 4; i++) v1.at(i)->Print();
    for (int i = 0; i < 4; i++) delete v1.at(i);
}

void copyAlgorithmExample() {
    vector<Leo*> v1;
    vector<Leo*> result(10);

    for (int i = 0; i < 10; i++) v1.push_back(new Leo);
    cout << "V1" << endl; for (int i = 0; i < 10; i++) v1.at(i)->Print();
    copy(v1.begin(), v1.end(), result.begin()); //copia só os ponteiros.

    cout << "RESULT" << endl;
    for (int i = 0; i < result.size(); i++) result.at(i)->Print();
    for (int i = 0; i < 10; i++) delete v1.at(i);
}

void mergeAlgorithmExample() {
    vector<Leo*> v1(5);
    vector<Leo*> v2(10);
    vector<Leo*> result(15);

    for (int i = 0; i < 5; i++) v1.at(i) = new Leo;
    for (int i = 0; i < 10; i++) v2.at(i) = new Leo;

    std::sort(v1.begin(), v1.end(), [](Leo* a, Leo * b) {return *a < *b;});
    std::sort(v2.begin(), v2.end(), [](Leo* a, Leo * b) {return *a < *b;});
    std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), result.begin(), [](Leo* a, Leo * b) {return *a < *b;});

    cout << "V1" << endl; for (int i = 0; i < 5; i++) v1.at(i)->Print();
    cout << "V2" << endl; for (int i = 0; i < 10; i++) v2.at(i)->Print();
    cout << "RESULT" << endl; for (int i = 0; i < result.size(); i++) result.at(i)->Print();

    for (int i = 0; i < result.size(); i++) delete result.at(i);
}

int main(int argc, char** argv) {
    sortAlgorithmExample();
    std::cout << std::endl << std::endl;
    copyAlgorithmExample();
    std::cout << std::endl << std::endl;
    mergeAlgorithmExample();
    std::cout << std::endl << std::endl;
    return (EXIT_SUCCESS);
}
